// ParticleSystemImpl.cpp -- particles system impl

#include "xsRendPCH.hpp"

#include "rend_ParticleSystemImpl.h"

namespace xs
{
Particle null_particle;

//=================================================================================================
// ParticleSystemImpl::ParticleSystemImpl
//=========

ParticleSystemImpl::ParticleSystemImpl(const ParticleSystemDesc &_desc, xs::Renderer *_xse):
desc(_desc), xse(_xse), accumulated_time(0), particles_created(0)
{
	// create new particles:
	if(desc.repeating == false)
	{
		particles.resize(desc.particle_count);
		for(unsigned int i=0; i<desc.particle_count; i++)
		{
			set_particle(particles[i]);
		}
	} else
	{
		particles.reserve(desc.particle_count * (u32)(ceil(desc.max_life_time)+0.5f));
	}
}

//=================================================================================================
// ParticleSystemImpl::~ParticleSystemImpl

ParticleSystemImpl::~ParticleSystemImpl()
{
}

//=================================================================================================
// ParticleSystemImpl::animate
//=========

bool ParticleSystemImpl::animate(const double &elapsed_ms)
{
	// animate particles
	for(unsigned int i=0; i<particles.size(); i++)
	{
		particles[i].energy -= (float)(elapsed_ms/particles[i].life_time);
		particles[i].rotation += (float)(particles[i].rotation_speed*elapsed_ms);

		particles[i].position += (float)(elapsed_ms)*particles[i].velocity;
	}

	// to moze byc wolne
	unsigned int i=0, j=0;
	for(i=0; i<particles.size(); i++)
	{
		if(i!=j) particles[j] = particles[i];
		if(particles[i].energy>0) j++;
	}
	
	j = i-j;
	while(j--) particles.pop_back();

	desc.time_to_stop_emit -= (int)(elapsed_ms*1000);
	if(desc.time_to_stop_emit<0)
		desc.emit = false;

	// add new ones:
	if(desc.repeating && desc.emit && desc.particle_count)
	{
		accumulated_time+=elapsed_ms;

		while(accumulated_time >= 1.0/desc.particle_count)
		{
			particles.push_back(null_particle);
			set_particle(particles.back());
			accumulated_time -= 1.0/desc.particle_count;
		}

	} else accumulated_time = 0;
	
	return particles.empty()==false;
}

//=================================================================================================
// ParticleSystemImpl::draw
//=========

void ParticleSystemImpl::draw()
{
	xse->set_texture_filter(xs::TF_LINEAR);

	quad.texture = desc.texture;
	quad.blend = desc.blend;

	for(int i=(int)particles.size()-1; i>=0; i--)
	{
		for(int j=0; j<4; j++)
		{
			quad.v[j].x = particles[i].position[0];
			quad.v[j].y = particles[i].position[1];
			const float &f = particles[i].energy;
			quad.v[j].color = 
				ARGB((u8)(get_alpha(desc.start_color)*f + (1.0f-f)*get_alpha(desc.end_color)),
				(u8)(get_red(desc.start_color)*f + (1.0f-f)*get_red(desc.end_color)),
				(u8)(get_green(desc.start_color)*f + (1.0f-f)*get_green(desc.end_color)),
				(u8)(get_blue(desc.start_color)*f + (1.0f-f)*get_blue(desc.end_color)));
		}

		float add = particles[i].energy * desc.start_size + (1.0f-particles[i].energy)*desc.dest_size;
		add /= 2;

		quad.v[0].x += add; quad.v[0].u = 1;
		quad.v[0].y -= add; quad.v[0].v = 0;

		quad.v[1].x += add; quad.v[1].u = 1;
		quad.v[1].y += add; quad.v[1].v = 1;

		quad.v[2].x -= add; quad.v[2].u = 0;
		quad.v[2].y += add; quad.v[2].v = 1;

		quad.v[3].x -= add; quad.v[3].u = 0;
		quad.v[3].y -= add; quad.v[3].v = 0;

		pos2 tmp;
		for(int j=0; j<4; j++)
		{
			tmp[0] = quad.v[j].x; tmp[1] = quad.v[j].y;
			tmp -= particles[i].position;
			tmp = cml::rotate_vector_2D(tmp, particles[i].rotation*xs::DEGTORAD);
			tmp += particles[i].position;
			quad.v[j].x = tmp[0]; quad.v[j].y = tmp[1];
		}

		xse->render_quad(quad);
	}
}

//=================================================================================================
// ParticleSystemImpl::set_particle
//=========

void ParticleSystemImpl::set_particle(Particle &p)
{
	p.rotation = rand(0.0f,1.0f)*360;
	p.energy = 1.0f;
	p.position = desc.position;
	p.position += pos2(rand(-2.0f, 2.0f), rand(-2.0f, 2.0f));
	p.life_time = rand(desc.min_life_time, desc.max_life_time);
	p.velocity = desc.direction * rand(0.5f, 1.0f);
	p.velocity = cml::rotate_vector_2D(p.velocity, rand(-desc.angle/2.0f,desc.angle/2)*DEGTORAD);
	p.rotation_speed = rand(desc.min_rotation, desc.max_rotation);

	particles_created++;
}

//=================================================================================================
// ParticleSystemImpl::get_desc
//=========

ParticleSystemDesc& ParticleSystemImpl::get_desc()
{
	return desc;
}

}